package com.gaogzhen.threadPool;

import lombok.extern.slf4j.Slf4j;

import java.util.HashSet;
import java.util.concurrent.TimeUnit;

/**
 * 自定义线程池
 */
@Slf4j(topic = "c.ThreadPool")
public class ThreadPool {
    /** 阻塞队列 */
    private final BlockingQueue<Runnable> taskQueue;
    /** 线程集合 */
    private final HashSet<Worker> workers = new HashSet();
    /** 核心线程数 */
    private final int coreSize;
    /** 超时时间 */
    private long timeout;
    /** 超时单位 */
    private TimeUnit unit;
    /** 决绝测试 */
    private RejectStrategy<Runnable> rejectStrategy;


    class Worker extends Thread{
        private Runnable task;


        public Worker(Runnable task) {
            this.task = task;
        }

        @Override
        public void run() {
            // 执行任务
            // 1、当task不为空时，执行任务
            // 2、当task为空时，尝试从任务队列获取任务执行
//            while (task != null || (task = taskQueue.take()) != null) {
            while (task != null || (task = taskQueue.pool(timeout, unit)) != null) {
                try {
                    log.debug("正在执行 {}", task);
                    task.run();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    task = null;
                }
            }
            synchronized (workers) {
                log.debug("移除 worker {}", this);
                workers.remove(this);
            }
        }
    }

    public ThreadPool(int coreSize, long timeout, TimeUnit unit, int queueCapacity, RejectStrategy<Runnable> strategy) {
        this.taskQueue = new BlockingQueue<>(queueCapacity);
        this.coreSize = coreSize;
        this.timeout = timeout;
        this.unit = unit;
        this.rejectStrategy = strategy;
    }

    /**
     * 执行任务
     * @param task      任务
     */
    public void execute(Runnable task) {
        // 当任务数没有超过核心线程数时，直接交给worker对象执行
        // 否则，暂存入任务队列
        synchronized (this) {
            if (workers.size() < coreSize) {
                Worker worker = new Worker(task);
                workers.add(worker);
                log.debug("新增 worker {}", worker);
                worker.start();
            } else {
                taskQueue.tryPut(rejectStrategy, task);
            }
        }
    }
}
